﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Net;


namespace AsyncFtp {
	using EventArgs;
	using System.Threading;

	public class AsyncFtp {
		public delegate void UploadFileProgressChangedHandler ( UploadFileProgressChangeEventdArgs e );
		public delegate void DownloadFileProgressChangedHandler ( DownloadFileProgressChangedEventArgs e );
		public delegate void DeleteFileHandler ( DeleteFileEventArgs e );
		public delegate void ListDirectoryHandler ( ListDirectoryEventArgs e );
		public delegate void CreateDirectoryHandler ( CreateDirectoryEventArgs e );

		public event UploadFileProgressChangedHandler UploadFileProgessChanged;
		public event DownloadFileProgressChangedHandler DownloadFileProgressChanged;
		public event DeleteFileHandler DeleteFile;
		public event ListDirectoryHandler ListDirectory;
		public event CreateDirectoryHandler CreateDirectory;

		public NetworkCredential Credential { get; private set; }
		public int BufferLength { get; set; }
		public bool UsePassive { get; set; }

		public AsyncFtp ( string username, string password, int bufferLength ) {
			this.Credential = new NetworkCredential( username, password );
			this.BufferLength = bufferLength;
		}

		public void Upload ( Uri ftpfile, string uploadfile ) {
			using ( FileStream fStream = new FileStream( uploadfile, FileMode.Open, FileAccess.Read ) )
				Upload( ftpfile, fStream );
		}
		public void Upload ( Uri ftpfile, byte[] file ) {
			using ( MemoryStream mStream = new MemoryStream( file ) )
				Upload( ftpfile, mStream );
		}
		public void Upload ( Uri ftpfile, Stream stream ) {
			ManualResetEvent waitObject;

			FtpState state = new FtpState();
			FtpWebRequest request = WebRequest.Create( ftpfile ) as FtpWebRequest;
			request.Method = WebRequestMethods.Ftp.UploadFile;
			request.Credentials = Credential;
			request.UsePassive = UsePassive;

			state.Request = request;
			waitObject = state.OperationComplete;

			Stream requestStream;
			try {
				requestStream = request.GetRequestStream();

				byte[] buffer = new byte[ BufferLength ];
				long count = 0;
				long readBytes = 0;
				long totalBytes = 0;

				totalBytes = stream.Length;

				do {
					count = stream.Read( buffer, 0, buffer.Length );
					requestStream.Write( buffer, 0, buffer.Length );
					readBytes += count;
					if ( UploadFileProgessChanged != null )
						UploadFileProgessChanged( new UploadFileProgressChangeEventdArgs( totalBytes, readBytes ) );
				} while ( count != 0 );

				requestStream.Close();
				state.Request.BeginGetResponse( new AsyncCallback( EndGetResponseCallBack ), state );
			} catch ( Exception ex ) {
				state.OperationException = ex;
				state.OperationComplete.Set();
				return;
			}

			if ( state.OperationException != null )
				throw state.OperationException;
		}

		public void Download ( Uri ftpfile, out byte[] file ) {
			ManualResetEvent waitObject;

			FtpState state = new FtpState();
			FtpWebRequest request = WebRequest.Create( ftpfile ) as FtpWebRequest;
			request.Method = WebRequestMethods.Ftp.DownloadFile;
			request.Credentials = this.Credential;
			request.UsePassive = UsePassive;

			state.Request = request;

			Stream responseStream;
			try {
				FtpWebResponse response = request.GetResponse() as FtpWebResponse;
				responseStream = response.GetResponseStream();

				byte[] buffer = new byte[ BufferLength ];
				long count = 0;
				long readBytes = 0;

				MemoryStream stream = new MemoryStream();

				do {
					count = responseStream.Read( buffer, 0, buffer.Length );
					stream.Write( buffer, 0, buffer.Length );
					readBytes += count;
					if ( DownloadFileProgressChanged != null )
						DownloadFileProgressChanged( new DownloadFileProgressChangedEventArgs( readBytes ) );
				} while ( count != 0 );

				responseStream.Close();
				file = stream.ToArray();
				state.Request.BeginGetResponse( new AsyncCallback( EndGetResponseCallBack ), state );
			} catch ( Exception ex ) {
				file = null;
				state.OperationException = ex;
				state.OperationComplete.Set();
				return;
			}

			if ( state.OperationException != null )
				throw state.OperationException;
		}
		public void Download ( Uri ftpfile, string downloadfile ) {
			byte[] buffer;
			Download( ftpfile, out buffer );
			using ( FileStream fStream = new FileStream( downloadfile, FileMode.Open, FileAccess.Write ) ) {
				fStream.Write( buffer, 0, buffer.Length );
			}
		}
		private void EndGetResponseCallBack ( IAsyncResult ar ) {
			FtpState state = ar.AsyncState as FtpState;
			FtpWebResponse response = null;
			try {
				response = state.Request.EndGetResponse( ar ) as FtpWebResponse;
				response.Close();
				state.StatusDescription = response.StatusDescription;
				state.OperationComplete.Set();
			} catch ( Exception ex ) {
				state.OperationException = ex;
				state.OperationComplete.Set();
			}
		}
	}
}
